/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.domain;

import com.kcp.ko.fmtnmlib.types.Copyable;
import org.hibernate.annotations.IndexColumn;

import javax.persistence.*;
import java.util.*;

/**
 * A Question referenced on a TestData.  This not only includes the question
 * as a string, but references to the answers, as well as the gradeQuestion
 * function used in grading a given test.
 * 
 * @author John Ericksen
 *
 */
@Entity
@Table( name="braintrain_questions" )
public class Question extends BrainTrainBase implements Copyable<Question>{
    
    private static final long serialVersionUID = 2445939838458193833L;
    
    private String question;
    private List<Answer> answers = new ArrayList<Answer>();
    private boolean multipleAnswer;
    private Integer questionOrder;
    private String incorrectResponse;
    
    //inverse mapping
    private TestData test;
    
    //transient
    private Long userAnswer;
    private boolean correct;
    private boolean unanswered;
    private Set<AnswerLog> answerLog;
    
    public Question(){}
    
    public Question(int answerSize){
        setupEmptyAnswers(answerSize);
    }

    public final void setupEmptyAnswers(int answerSize){
        if(answerSize > 0)
        {
            for(int i = 0; i < answerSize; i++) {
                getAnswers().add(new Answer());
            }

            //if none are already correct, make the first correct
            boolean foundCorrect = false;
            for(Answer a : answers) {
                if(a.isCorrect()){
                    foundCorrect = true;
                }
            }

            if(!foundCorrect) {
                getAnswers().get(0).setCorrect(true);
            }
        }
    }



    @OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn( name="question_id" )
    @IndexColumn(name = "answerOrder", base=0)
    public List<Answer> getAnswers() {
        return answers;
    }
    public void setAnswers(List<Answer> answers) {
        this.answers = answers;
    }

    @Column(length=4000)
    public String getQuestion() {
        return question;
    }
    public void setQuestion(String question) {
        this.question = question;
    }
    public Integer getQuestionOrder() {
        return questionOrder;
    }
    public void setQuestionOrder(Integer questionOrder) {
        this.questionOrder = questionOrder;
    }
    public boolean isMultipleAnswer() {
        return multipleAnswer;
    }
    public void setMultipleAnswer(boolean multipleAnswer) {
        this.multipleAnswer = multipleAnswer;
    }

    @Column(length=4000)
    public String getIncorrectResponse() {
        return incorrectResponse;
    }
    public void setIncorrectResponse(String incorrectResponse) {
        this.incorrectResponse = incorrectResponse;
    }
    
    @Transient
    public Long getUserAnswer() {
        return userAnswer;
    }
    public void setUserAnswer(Long userAnswer) {
        this.userAnswer = userAnswer;
        if(userAnswer != null)
        {
            for(Answer a: getAnswers())
            {
                if(a.getId().equals(userAnswer))
                {
                    a.setUserAnswer(true);
                }
                else
                {
                    a.setUserAnswer(false);
                }
            }
        }
    }
    


    @ManyToOne
    @JoinColumn(name="test_id", insertable=false, updatable=false)
    public TestData getTest() {
        return test;
    }
    public void setTest(TestData test) {
        this.test = test;
    }
    
    public boolean gradeQuestion(User user) {
        correct = true;
        unanswered  = true;
        answerLog = new HashSet<AnswerLog>();
        
        for(Answer answer: getAnswers())
        {
            if(answer.isUserAnswer())
            {
                unanswered = false;
                
                AnswerLog log = new AnswerLog();
                log.setSubmitDate(new Date(System.currentTimeMillis()));
                log.setAnswer(answer);
                log.setUser(user);
                answerLog.add(log);
                
            }
            
            if(answer.isCorrect() != answer.isUserAnswer())
            {
                correct = false;
            }
        }
        
        for(AnswerLog l : answerLog){
            l.setCorrect(correct);
        }
        
        return correct;
    }

    @Transient
    public boolean isUnanswered() {
        return unanswered;
    }

    @Transient
    public boolean isCorrect() {
        return correct;
    }

    @Transient
    public Set<AnswerLog> getAnswerLog() {
        return answerLog;
    }
    
    @Transient
    public String getResponse()
    {
        if(isUnanswered())
        {
            return "Unanswered";
        }
        else if(isCorrect())
        {
            return "Correct";
        }
        else
        {
            return getIncorrectResponse();
        }
    }

    @Transient
    public void resetIndexes() {
        int index = 0;
        for(Answer a: getAnswers())
        {
            a.setAnswerOrder(index++);
        }
    }

    @Transient
    public Question copy() {
        Question copy = new Question();

        copy.setQuestion(this.getQuestion());
        copy.setMultipleAnswer(this.isMultipleAnswer());
        copy.setQuestionOrder(this.getQuestionOrder());
        copy.setIncorrectResponse(this.getIncorrectResponse());

        for(Answer answer: this.getAnswers())
        {
            copy.getAnswers().add(answer.copy());
        }
        copy.setVersion(this.getVersion());

        return copy;
    }

    @Transient
    public String toString() {
        
        StringBuffer buff = new StringBuffer();

        buff.append(" " + getQuestion() + "\n");

        for(Answer a: getAnswers())
        {
            buff.append(a.toString());
        }

        return buff.toString();
    }

    public void resetQuestion() {
        userAnswer = null;
        correct = true;

        if(answers == null) {
            answers = new ArrayList<Answer>();
        }
        for(Answer a : answers) {
            a.resetAnswer();
        }
    }
}
